---
title: 雪花算法
description: 分布式唯一id
---

自增 id 不适合分布式数据库，分库分表场景。只适合小型项目。

UUID 会影响索引效率，因为 UUID 是无序的，用一堆无序的 ID 来构建一个有序的索引目录，性能上肯定会有问题的。


分布式场景中，Snowflake 可以按时间顺序生成全局唯一的 Id。需要为每个数据中心和机器编号，保证每台机器生成的 Id 不重复。


## 详解雪花算法

![image-20230401152757798](https://img.wkq.pub/pic/image-20230401152757798.png)

最高位不用，0 表示正值，中间 41 位是时间戳；接下来是 10 位工作机器id，分为两个部分，数据中心id和机器 id；最后是 12bit 序列号，是为了解决一台机器内同一毫秒生成多个 id 的问题。

同一台机器同一毫秒时间戳下，可以生成 2^12 =4096 个不重复的 id。肯定是够用的。



## 雪花算法问题

* 数据中心，机器 ID 怎么设置？ 在机器启动时，设置为全局变量。可以通过两种方式设置。利用 redis 自增设置；或者利用数据库，为每台机器分配 workId，保存 ip 和workId 的关系。
* 时钟回拨问题，

## 起始时间戳


在分布式系统中，不同的机器可能会在不同的时间戳上生成 ID，这可能会导致不同机器生成的 ID 之间存在顺序上的不一致，影响到一些需要按照时间顺序进行排序的场景，比如日志的记录、消息的处理等。

为了解决这个问题，我们引入了起始时间戳 epoch，通过让所有机器生成 ID 的时间戳都以 epoch 为起始时间戳，就能够保证生成的 ID 之间存在顺序上的一致性。同时，由于使用了起始时间戳，所以可以把时间戳从 41 位减少到 39 位，为序列号和节点 ID 留出更多的空间，使得生成的 ID 更具唯一性和随机性。

由于 41位是时间戳，我们的时间计算是从 1970 年开始的，所以只能使用 69 年（2^41毫秒）

 :::tip

1. 计算 2^41 毫秒对应的秒数：2^41 / 1000 = 2199023255 秒
2. 计算秒数对应的分钟数：2199023255 / 60 = 36650387.58 分钟
3. 计算分钟数对应的小时数：36650387.58 / 60 = 610839.79 小时
4. 计算小时数对应的天数：610839.79 / 24 = 25451.658 天
5. 计算天数对应的年数：25451.658 / 365 = 69.767 年

:::
